/**
 * Copyright (c) 2022 qigaohua168@163.com.
 * All rights reserved.
 *
 * Created on 2022/12/29 11:44.
 */

#include <thread>
#include "qlog.h"

// #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wunused-parameter"

namespace qlibs {
namespace qlog {

const char * LogEX::what() const noexcept { return _msg.c_str(); }

namespace formatter {

Formatter::~Formatter() {  }

FlagFormatter::~FlagFormatter() {  }

void UserFormatter::format(const log_msg& msg, const std::tm&, log_buffer_t &buff) {
    buff.append(_show);
}

}

void FileHelper::open(const std::string& filename)
{
    close();

    _filename = filename;

    if (_event_handlers.before_open) {
        _event_handlers.before_open(filename);
    }

    for (int tries = 0; tries < _open_tries; ++tries) {
        _fp = fopen(filename.c_str(), "wb");
        if (_fp) {
            if (_event_handlers.after_open) {
                _event_handlers.after_open(filename, _fp);
            }
            return;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    throw_log_ex("Failed to open file " + filename + " for write", errno);
}


void FileHelper::reopen()
{
    if (_filename.empty()) {
        throw_log_ex("Failed to reopen file -- filename is empty.");
    }
    this->open(_filename);
}


void FileHelper::close()
{
    if (nullptr != _fp) {
        if (_event_handlers.before_close) {
            _event_handlers.before_close(_filename, _fp);
        }
        ::fclose(_fp);
        _fp = nullptr;

        if (_event_handlers.after_close) {
            _event_handlers.after_close(_filename);
        }
    }
}

void FileHelper::flush()
{
    ::fflush(_fp);
}



namespace sink {
void Sink::set_level(log_level level)
{
    _level.store(level, std::memory_order_relaxed);
}

log_level Sink::level() const
{
    return static_cast<log_level>(_level.load(), std::memory_order_relaxed);
}

bool Sink::should_log(log_level msg_level) const
{
    return msg_level >= _level.load(std::memory_order_relaxed);
}


}


// void Logger::_log(source_loc loc, log_level level, const char *fmt, ...)
// {
//     bool log_enabled = should_log(level);

//     if (log_enabled) {
//         char buff[512];

//         va_list ap;
//         va_start(ap, fmt);
//         int buff_len = vsnprintf(buff, sizeof(buff), fmt, ap);
//         buff[buff_len] = '\0';
//         va_end(ap);

//         log_msg log_msg(loc, _name.c_str(), level, buff);

//         for (auto &sink : _sinks) {
//             if (sink->should_log(log_msg._level)) {
//                 sink->log(log_msg);
//             }
//         }
//     }
// }

void Logger::_log(source_loc loc, enum log_level level, const char *fmt, va_list args)
{
    bool log_enabled   = should_log(level);
    bool flush_enabled = should_flush(level);

    if (log_enabled) {
        char buff[512];

        int buff_len   = vsnprintf(buff, sizeof(buff), fmt, args);
        buff[buff_len] = '\0';

        log_msg log_msg(loc, _name.c_str(), level, buff);

        for (auto &sink : _sinks) {
            if (sink->should_log(log_msg._level)) {
                sink->log(log_msg);
            }
        }
    }

    if (flush_enabled) {
        for (auto &sink : _sinks) {
            sink->flush();
        }
    }
}

} // namespace qlog

} // namespace qlibs

// #pragma GCC diagnostic pop


/////////////////////////////////////////////


namespace test {
using namespace qlibs::qlog;
using namespace qlibs::qlog::sink;

static std::shared_ptr<Logger> logger;

void thread1()
{
    sleep(1);
    for (int i = 0; i < 100; ++i) {
        logger->info("hello world %d!!", 1);
    }
}

void thread2()
{
    sleep(1);
    for (int i = 0; i < 100; ++i) {
        logger->info("hello world %d!!", 2);
    }
}

int test()
{
    logger = std::make_shared<Logger>("test", std::make_shared<basic_file_sink_st>("test.log"));

    std::shared_ptr<basic_file_sink_mt> sink2(new basic_file_sink_mt("test2.log"));
    sink2->set_pattern("[%D %T] %v");
    logger->add_sink(std::move(sink2));

    // std::shared_ptr<console_stdout_sink_st> sink3(new console_stdout_sink_st());
    std::shared_ptr<console_stdout_sink_mt> sink3(new console_stdout_sink_mt());
    // sink3->set_pattern("[%L] %^[%D %T]%$ %v");
    sink3->set_pattern("[%^%L%$] [%D %T] %v");
    logger->add_sink(std::move(sink3));

    // logger->set_pattern("%C %v");

    // Logger logger("test", std::make_shared<basic_file_sink_mt>("test.log"));
    // Logger logger("test", std::make_shared<basic_file_sink_st>("test.log"));

    logger->info("hello world %d!!", 1234);
    logger->debug("hello world %d!!", 1234);

    logger->log(source_loc{__FILE__, __FUNCTION__, __LINE__}, log_level::error,
            "hello world %d %s!!", 1234, "abc");

    std::thread t1(thread1);
    std::thread t2(thread2);

    t1.join();
    t2.join();

    return 0;
}

int test2()
{
    using namespace qlibs;
    Logger::ptr_t logger = std::make_shared<Logger>("test", std::make_shared<basic_file_sink_st>("test.log"));
    register_logger(std::move(logger));

    Qlog::instance().default_logger()->info("hello world %d %s!!", 123, "abc");
    Qlog::instance().default_logger()->warn("hello world %d %s!!", 123, "abc");
    Qlog::instance().default_logger()->error("hello world %d %s!!", 123, "abc");
    Qlog::instance().default_logger()->alert("hello world %d %s!!", 123, "abc");
    // Qlog::instance().default_logger()->log(source_loc{__FILE__, __FUNCTION__, __LINE__},
    //         log_level::error, "hello world %d %s!!", 123, "abc");

    QLOG(log_level::error, "hello world %d %s!!", 123, "abc");
    QLOG(log_level::warn,  "hello world %d %s!!", 123, "abc");
    QLOG(log_level::info,  "hello world %d %s!!", 123, "abc");
    QLOG(log_level::debug, "hello world %d %s!!", 123, "abc");

    QTRACE("hello world %d %s!!", 123, "abc");
    QDEBUG("hello world %d %s!!", 123, "abc");
    QINFO ("hello world %d %s!!", 123, "abc");
    QERROR("hello world %d %s!!", 123, "abc");
    QASSERT("hello world %d %s!!", 123, "abc");

    QINFO ("hello world!!");
    QERROR("hello world!!");


    QLOGGER_TRACE(get_logger_raw("test"), "hello world %d %s!!", 123, "abc");
    QLOGGER_DEBUG(get_logger_raw("test"), "hello world %d %s!!", 123, "abc");
    QLOGGER_INFO (get_logger_raw("test"), "hello world %d %s!!", 123, "abc");
    QLOGGER_ERROR(get_logger_raw("test"), "hello world %d %s!!", 123, "abc");
    QLOGGER_ASSERT(get_logger_raw("test"), "hello world %d %s!!", 123, "abc");

    return 0;
}


}

int main()
{
    test::test2();
    return 0;
}



